JS设计模式之原型模式

前言

我最初是通过慕课网上的《JavaScript 设计模式系统讲解与应用》课程了解设计模式的。接着便是读书来深入理解。这篇算是关于《JavaScript 设计模式与开发实践》的读书笔记。

原型模式

原型模式是用于创建对象的一种模式。至于创建对象的方式就是借助原型对已有对象进行克隆。但是这种克隆方式是一种浅拷贝。

关于原型模式有这么一个例子可以帮助我们去理解为什么要克隆已有对象。

假设有一个飞机游戏,你获得了一个分身技能,当你使用分身技能的时候,需要在页面中创建出一个和当前飞机一模一样的飞机。如果不使用原型模式,在创建分身之前,你需要保存当前飞机的各种状态和属性。随后将这些状态和属性设置到分身飞机上面。

如果使用原型模式,我们只需要调用一下克隆对象的方法就可以了。

具体使用(Es6语法)

// 当前飞机对象
let plane = {
lev: 10,
state: ['攻击力', '血量']
}

// 克隆对象的方法
const clone = obj => {
let F = function(){};
F.prototype = obj;
return new F();
}

let planeClone = clone(plane);
console.log(planeClone.lev); // 输出10

// 引用类型的原型属性会被实例共享
planeClone.state.push('防御力');
console.log(plane); // 输出:攻击力,血量,防御力

从这段代码中我们可以看到将克隆对象作为函数F的原型,接着返回函数F的实例。

planeClone这个对象本身是没有任何属性的,之所以可以输出lev属性值,是因为 JS 语言的原型链机制。

每当代码读取到对象的某个属性的时候,都会进行一次搜索。搜索先从实例本身开始,实例本身没有的话,就会搜索实例的原型对象。如果原型对象有的话则返回,没有的话,则会继续向上搜索。对象有__proto__属性,对象的__proto__属性完全等于构造函数的prototype属性

根据这样的原理,为什么会输出lev的属性值可以这样解释:

  • planeClone这个对象的属性进行搜索,但是没有找到lev属性。
  • 接着便会查找planeClone.__proto__所指向的F.prototype
  • 也就是构造函数的原型。
  • F.prototype就等于plane
  • plane这个对象中找到了lev属性,并返回了它的值。

另外值得一提的是,引用类型的原型属性会被实例共享,这也是原型模式的一个缺点。

至于如何解决这个缺点可以去看看《javascript 高级程序设计》这本书对原型继承的讲解。

也可以进入传送门

Es5对原型模式的实现

// Es5中原型模式
let plane = {
lev: 10,
state: ['攻击力', '血量']
}

let planeClone = Object.create(plane);

Es5的写法看起来更简单一些,只需要调用Object.create方法即可。不过有一点要注意的是,Object.create创建对象的效率并不高,通常比通过构造函数创建对象要慢。